package com.mrdeer.merchant.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mrdeer.common.domain.BusinessException;
import com.mrdeer.common.domain.CommonErrorCode;
import com.mrdeer.common.domain.PageVo;
import com.mrdeer.common.util.PhoneUtil;
import com.mrdeer.common.util.StringUtil;
import com.mrdeer.merchant.api.MerchantService;
import com.mrdeer.merchant.api.dto.*;
import com.mrdeer.merchant.api.entity.Merchant;
import com.mrdeer.merchant.api.entity.Staff;
import com.mrdeer.merchant.api.entity.Store;
import com.mrdeer.merchant.api.entity.StoreStaff;
import com.mrdeer.merchant.convert.MerchantConvert;
import com.mrdeer.merchant.convert.StaffConvert;
import com.mrdeer.merchant.convert.StoreConvert;
import com.mrdeer.merchant.mapper.MerchantMapper;
import com.mrdeer.merchant.mapper.StaffMapper;
import com.mrdeer.merchant.mapper.StoreMapper;
import com.mrdeer.merchant.mapper.StoreStaffMapper;
import com.mrdeer.user.api.AuthorizationService;
import com.mrdeer.user.api.TenantService;
import com.mrdeer.user.api.dto.authorization.RoleDTO;
import com.mrdeer.user.api.dto.tenant.CreateAccountRequestDTO;
import com.mrdeer.user.api.dto.tenant.CreateTenantRequestDTO;
import com.mrdeer.user.api.dto.tenant.TenantDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * @author ：xie jianchu
 * @date ：Created in 2020/9/10 0010 11:06
 */
@Slf4j
@Service
public class MerchantServiceImpl implements MerchantService {

    @Autowired
    private MerchantMapper merchantMapper;

    @Autowired
    private StoreMapper storeMapper;

    @Autowired
    private StaffMapper staffMapper;

    @Autowired
    private StoreStaffMapper storeStaffMapper;

    @Reference
    private TenantService tenantService;

    @Override
    public MerchantDTO queryMerchantById(Long id) throws BusinessException {
        Merchant merchant = merchantMapper.selectById(id);
        return MerchantConvert.INSTANCE.entity2DTO(merchant);
    }

    /**
     * 根据租户ID查询商户信息
     *
     * @param tenantId 租户ID
     * @return
     */
    @Override
    public MerchantDTO queryMerchantByTenantId(Long tenantId) {
        Merchant merchant = merchantMapper.selectOne(new QueryWrapper<Merchant>().lambda().eq(Merchant::getTenantId, tenantId));
        return MerchantConvert.INSTANCE.entity2DTO(merchant);
    }

    /**
     * 注册商户服务接口，接收账号、密码、手机号，为了可扩展性使用MerchantDTO接收数据
     * 调用SaaS接口:新增租户、用户、绑定租户和用户的关系，初始化权限
     *
     * @param merchantDTO 商户注册信息
     * @return
     * @throws BusinessException
     */
    @Override
    @Transactional
    public MerchantDTO createMerchant(MerchantDTO merchantDTO) throws BusinessException {
        if (merchantDTO == null) {
            throw new BusinessException(CommonErrorCode.E_200201);
        }
        if (StringUtils.isBlank(merchantDTO.getMobile())) {
            throw new BusinessException(CommonErrorCode.E_200230);
        }
        if (!PhoneUtil.isMatches(merchantDTO.getMobile())) {
            throw new BusinessException(CommonErrorCode.E_200224);
        }
        /**
         * 校验手机号的唯一性
         * 根据手机号查询商户表，如果存在记录则说明手机号已存在
         */
        Integer count = merchantMapper.selectCount(new LambdaQueryWrapper<Merchant>().eq(Merchant::getMobile, merchantDTO.getMobile()));
        if (count > 0) {
            throw new BusinessException(CommonErrorCode.E_200203);
        }
        //如果商户不存在，调用SaaS接口
        CreateTenantRequestDTO createTenantRequestDTO = new CreateTenantRequestDTO();
        createTenantRequestDTO.setMobile(merchantDTO.getMobile());
        createTenantRequestDTO.setUsername(merchantDTO.getUsername());
        createTenantRequestDTO.setPassword(merchantDTO.getPassword());
        createTenantRequestDTO.setTenantTypeCode("iot-merchant");       //租户类型
        createTenantRequestDTO.setBundleCode("iot-merchant");           //套餐，根据套餐进行分配权限
        createTenantRequestDTO.setName(merchantDTO.getUsername());      //租户名称，和账号名一样

        TenantDTO tenantDTO = tenantService.createTenantAndAccount(createTenantRequestDTO);

        //如果租户在SaaS已经存在，SaaS直接返回此租户的信息，否则直接添加
        if (tenantDTO == null || tenantDTO.getId() == null) {
            throw new BusinessException(CommonErrorCode.E_200012);
        }
        //租户ID
        Long tenantId = tenantDTO.getId();
        //租户ID在商户表中唯一
        //根据租户ID从商户表查询，如果存在记录，则不允许添加商品
        Integer count1 = merchantMapper.selectCount(new LambdaQueryWrapper<Merchant>().eq(Merchant::getTenantId, tenantId));
        if (count1 > 0) {
            //商户在当前租户下已经注册，不可重复注册
            throw new BusinessException(CommonErrorCode.E_200017);
        }
        Merchant merchant = MerchantConvert.INSTANCE.dto2Entity(merchantDTO);
        merchant.setAuditStatus("0");
        merchant.setTenantId(tenantId);     //设置商户租户ID
        merchantMapper.insert(merchant);

        //新增门店
        StoreDTO storeDTO = new StoreDTO();
        storeDTO.setStoreName("根门店");
        storeDTO.setMerchantId(merchant.getId());
        StoreDTO store = createStore(storeDTO);

        //新增员工
        StaffDTO staffDTO = new StaffDTO();
        staffDTO.setStaffStatus(true);
        staffDTO.setMobile(merchantDTO.getMobile());
        staffDTO.setUsername(merchantDTO.getUsername());        //账号
        staffDTO.setStoreId(store.getId());                     //员工所属门店ID
        staffDTO.setMerchantId(merchant.getId());               //员工所属商户ID
        StaffDTO staff = createStaff(staffDTO);

        //为门店设置管理员
        bindStaffToStore(store.getId(), staff.getId());
        return MerchantConvert.INSTANCE.entity2DTO(merchant);
    }

    /**
     * 商户资质申请
     *
     * @param merchantId  商户ID
     * @param merchantDTO 资质申请信息
     * @throws BusinessException
     */
    @Override
    @Transactional
    public void applyMerchant(Long merchantId, MerchantDTO merchantDTO) throws BusinessException {
        if (merchantId == null || merchantDTO == null) {
            throw new BusinessException(CommonErrorCode.E_300009);
        }
        //校验merchantId的合法性，查询商户表，如果查询不到记录，认为非法
        Merchant merchant = merchantMapper.selectById(merchantId);
        if (merchant == null) {
            throw new BusinessException(CommonErrorCode.E_200002);
        }
        //将dto转成Entity
        Merchant entity = MerchantConvert.INSTANCE.dto2Entity(merchantDTO);
        //调用Mapper更新商户表
        //更新之前需要将表的参数设备到entity
        entity.setId(merchantId);
        entity.setMobile(merchant.getMobile());     //因为在资质申请的时候，手机号不允许修改
        entity.setAuditStatus("1");                 //审核状态:1-已申请 2-待审核
        entity.setTenantId(merchant.getTenantId());

        merchantMapper.updateById(entity);
    }

    /**
     * 新增门店
     *
     * @param storeDTO 门店信息
     * @return
     * @throws BusinessException
     */
    @Override
    public StoreDTO createStore(StoreDTO storeDTO) throws BusinessException {
        Store entity = StoreConvert.INSTANCE.dto2Entity(storeDTO);
        log.info("新增门店:{}", JSON.toJSONString(entity));
        //新增门店
        storeMapper.insert(entity);
        return StoreConvert.INSTANCE.entity2Dto(entity);
    }

    /**
     * 门店列表的查询
     *
     * @param storeDTO 查询条件，必要参数:商户ID
     * @param pageNo   页码
     * @param pageSize 分页记录数
     * @return
     */
    @Override
    public PageVo<StoreDTO> queryStoreByPage(StoreDTO storeDTO, Integer pageNo, Integer pageSize) {
        //分页条件
        Page<Store> page = new Page<>(pageNo, pageSize);
        //查询条件拼装
        LambdaQueryWrapper<Store> lambdaQueryWrapper = new LambdaQueryWrapper<Store>();
        //如果传入商店ID，此时要拼装 查询条件
        if (storeDTO != null && storeDTO.getMerchantId() != null) {
            lambdaQueryWrapper.eq(Store::getMerchantId, storeDTO.getMerchantId());
        }
        //再拼装其他查询条件，比如:门店名称
        if (storeDTO != null && StringUtils.isNotEmpty(storeDTO.getStoreName())) {
            lambdaQueryWrapper.like(Store::getStoreName, storeDTO.getStoreName());
        }
        //反野查询数据库
        IPage<Store> storeIPage = storeMapper.selectPage(page, lambdaQueryWrapper);
        //查询列表
        List<Store> records = storeIPage.getRecords();
        //将包含entity的list转成包含dto的list
        List<StoreDTO> storeDTOS = StoreConvert.INSTANCE.listEntity2Dto(records);
        return new PageVo<>(storeDTOS, storeIPage.getTotal(), pageNo, pageSize);
    }

    /**
     * 校验门店是否属于某商户
     *
     * @param storeId    门店ID
     * @param merchantId 商户ID
     * @return true 属于 false不属于
     */
    @Override
    public Boolean queryStoreInMerchant(Long storeId, Long merchantId) {
        Integer count = storeMapper.selectCount(new LambdaQueryWrapper<Store>().eq(Store::getId, storeId).eq(Store::getMerchantId, merchantId));
        return count > 0;
    }

    @Override
    public PageVo<MerchantDTO> queryMerchantPage(MerchantQueryDTO merchantQueryDTO, Integer pageNo, Integer pageSize) throws BusinessException {
        IPage<Merchant> page = new Page<>(pageNo == null ? 1 : pageNo, pageSize == null ? 10 : pageSize);
        LambdaQueryWrapper<Merchant> lambdaQueryWrapper = new LambdaQueryWrapper<Merchant>();
        if (merchantQueryDTO != null) {
            lambdaQueryWrapper.like(StringUtils.isNotBlank(merchantQueryDTO.getMerchantName()), Merchant::getMerchantName, merchantQueryDTO.getMerchantName())
                    .eq(StringUtils.isNotBlank(merchantQueryDTO.getMobile()), Merchant::getMobile, merchantQueryDTO.getMobile())
                    .eq(StringUtils.isNotBlank(merchantQueryDTO.getMerchantType()), Merchant::getMerchantType, merchantQueryDTO.getMerchantType())
                    .eq(merchantQueryDTO.getAuditStatus() != null, Merchant::getAuditStatus, merchantQueryDTO.getAuditStatus());
        }
        IPage<Merchant> result = merchantMapper.selectPage(page, lambdaQueryWrapper);
        if (result.getTotal() > 0) {
            List<MerchantDTO> merList = MerchantConvert.INSTANCE.entityList2DtoList(result.getRecords());
            return new PageVo<MerchantDTO>(merList, result.getTotal(), new Long(result.getCurrent()).intValue(), new Long(result.getSize()).intValue());
        }
        return new PageVo<MerchantDTO>(new ArrayList<MerchantDTO>(), 0, pageNo, pageSize);
    }

    @Override
    public void verifyMerchant(Long merchantId, String auditStatus) throws BusinessException {
        Merchant merchant = merchantMapper.selectById(merchantId);
        if (merchant == null) {
            throw new BusinessException(CommonErrorCode.E_100108);
        }
        merchant.setAuditStatus(auditStatus);
        merchantMapper.updateById(merchant);
    }

    @Override
    public StoreDTO createStore(StoreDTO storeDTO, List<Long> staffIds) throws BusinessException {
        //设置根门店
        Long rootStoreId = getRootStore(storeDTO.getMerchantId());
        storeDTO.setParentId(rootStoreId);
        //新增门店
        Store store = StoreConvert.INSTANCE.dto2Entity(storeDTO);
        storeMapper.insert(store);
        if (staffIds != null) {
            //设置管理员
            staffIds.forEach(id -> {
                bindStaffToStore(store.getId(), id);
            });
        }
        return StoreConvert.INSTANCE.entity2Dto(store);
    }

    /**
     * 新增员工
     *
     * @param staffDTO 员工信息
     * @return 新增成功的员工 信息
     * @throws BusinessException
     */
    @Override
    public StaffDTO createStaff(StaffDTO staffDTO) throws BusinessException {
        //参数合法性校验
        if (staffDTO == null || StringUtils.isBlank(staffDTO.getMobile()) || StringUtil.isBlank(staffDTO.getUsername()) || staffDTO.getStoreId() == null) {
            throw new BusinessException(CommonErrorCode.E_300009);
        }
        //在同一个商户下员工的账号唯一
        if (isExistStaffByUsername(staffDTO.getUsername(), staffDTO.getMerchantId())) {
            throw new BusinessException(CommonErrorCode.E_200020);
        }
        //在同一个商户下员工的手机号唯一
        if (isExistStaffByMobile(staffDTO.getMobile(), staffDTO.getMerchantId())) {
            throw new BusinessException(CommonErrorCode.E_200019);
        }
        Staff staff = StaffConvert.INSTANCE.dto2Entity(staffDTO);
        staffMapper.insert(staff);
        return StaffConvert.INSTANCE.entity2Dto(staff);
    }

    /**
     * 查询根门店
     *
     * @param merchantId
     * @return
     */
    private Long getRootStore(Long merchantId) {
        Store store = storeMapper.selectOne(new QueryWrapper<Store>().lambda().eq(Store::getMerchantId, merchantId).isNull(Store::getParentId));
        if (store == null) {
            throw new BusinessException(CommonErrorCode.E_200014);
        }
        return store.getId();
    }

    @Override
    public void bindStaffToStore(Long storeId, Long staffId) throws BusinessException {

    }

    @Override
    public PageVo<StaffDTO> queryStaffByPage(StaffDTO staffDTO, Integer pageNo, Integer pageSize) throws BusinessException {
        //创建分页
        Page<Staff> page = new Page<>(pageNo, pageSize);
        //构建查询条件
        QueryWrapper<Staff> qw = new QueryWrapper<>();
        if (staffDTO != null) {
            if (staffDTO.getMerchantId() != null) {
                qw.lambda().eq(Staff::getMerchantId, staffDTO.getMerchantId());
            }
            if (StringUtils.isNotBlank(staffDTO.getUsername())) {
                qw.lambda().like(Staff::getUsername, staffDTO.getUsername());
            }
            if (StringUtils.isNotBlank(staffDTO.getFullName())) {
                qw.lambda().like(Staff::getFullName, staffDTO.getFullName());
            }
            if (staffDTO.getStaffStatus() != null) {
                qw.lambda().eq(Staff::getStaffStatus, staffDTO.getStaffStatus());
            }
        }
        //执行订单
        IPage<Staff> staffIPage = staffMapper.selectPage(page, qw);
        //Entity List 转 DTO List
        List<StaffDTO> staffList = StaffConvert.INSTANCE.listEntity2Dto(staffIPage.getRecords());
        //封装结果集
        PageVo<StaffDTO> staffDTOS = new PageVo<>(staffList, staffIPage.getTotal(), pageNo, pageSize);
        if (staffDTOS.getCounts() == 0) {
            return staffDTOS;
        }
        //添加员工所在门店信息
        for (StaffDTO staffs : staffDTOS) {
            StoreDTO storeDTO = queryStoreById(staffs.getStoreId());
            if (storeDTO != null) {
                staffs.setStoreName(storeDTO.getStoreName());
            }
        }
        return staffDTOS;
    }

    /**
     * @param id ID
     * @return
     * @throws BusinessException
     */
    @Override
    public StoreDTO queryStoreById(Long id) throws BusinessException {
        Store store = storeMapper.selectById(id);
        StoreDTO storeDTO = StoreConvert.INSTANCE.entity2Dto(store);
        if (storeDTO != null) {
            List<StaffDTO> staffs = queryStoreAdmin(id);
            storeDTO.setStaffs(staffs);
        }
        return storeDTO;
    }

    /**
     * 获取门店管理员列表
     *
     * @param storeId
     * @return
     */
    private List<StaffDTO> queryStoreAdmin(Long storeId) {
        //根据门店获取管理员ID
        List<StoreStaff> storeStaffs = storeStaffMapper.selectList(new QueryWrapper<StoreStaff>().lambda().eq(StoreStaff::getStoreId, storeId));
        List<Staff> staff = null;
        if (!storeStaffs.isEmpty()) {
            List<Long> staffIds = new ArrayList<>();
            //查询结果不为空，则遍历获取管理员ID，可能有多个
            storeStaffs.forEach(ss -> {
                Long staffId = ss.getStaffId();
                staffIds.add(staffId);
            });
            //根据ID获取管理员信息
            staff = staffMapper.selectBatchIds(staffIds);
        }
        return StaffConvert.INSTANCE.listEntity2Dto(staff);
    }

    @Override
    public void modifyStore(StoreDTO storeDTO, List<Long> staffIds) throws BusinessException {
        //更新门店的信息
        Store store = StoreConvert.INSTANCE.dto2Entity(storeDTO);
        storeMapper.updateById(store);
        if (staffIds != null) {
            //清除门店绑定的管理员
            storeStaffMapper.delete(new QueryWrapper<StoreStaff>().lambda().eq(StoreStaff::getStoreId, storeDTO.getId()));
            //重新绑定关系设置，管理员
            staffIds.forEach(id -> {
                bindStaffToStore(store.getId(), id);
            });
        }
    }

    @Override
    @Transactional
    public void removeStore(Long id) throws BusinessException {
        Store store = storeMapper.selectById(id);
        //根门店不可删除
        if (store.getParentId() == null) {
            throw new BusinessException(CommonErrorCode.E_200018);
        }
        //清除门店和员工关系
        storeStaffMapper.delete(new QueryWrapper<StoreStaff>().lambda().eq(StoreStaff::getStoreId, id));
        //清除员工所属的门店
        staffMapper.update(null, new UpdateWrapper<Staff>().lambda().eq(Staff::getStoreId, id).set(Staff::getStoreId, null));
        //删除 门店
        storeMapper.deleteById(id);
    }

    @Override
    @Transactional
    public void createStaffAndAccount(StaffDTO staffDTO, String[] roleCodes) throws BusinessException {
        //1.新增员工
        createStaff(staffDTO);
        //获取商户的租户ID
        Long tenantId = queryMerchantById(staffDTO.getMerchantId()).getTenantId();
        CreateAccountRequestDTO accountRequestDTO = new CreateAccountRequestDTO();
        accountRequestDTO.setMobile(staffDTO.getMobile());
        accountRequestDTO.setUsername(staffDTO.getUsername());
        accountRequestDTO.setPassword(staffDTO.getPassword());

        //在租户内创建账号并绑定角色，包含校验账号是否存在及角色是否绑定
        tenantService.checkCreateStaffAccountRole(tenantId, accountRequestDTO, roleCodes);
    }

    @Reference
    private AuthorizationService authService;

    @Override
    public StaffDTO queryStaffDetail(Long id, Long tenantId) {
        StaffDTO staffDTO = queryStaffById(id);
        //根据用户名和租户ID查询角色信息
        List<RoleDTO> roles = authService.queryRolesByUsername(staffDTO.getUsername(), tenantId);
        List<StaffRoleDTO> staffRoles = new ArrayList<>();
        if (!roles.isEmpty()) {
            roles.forEach(roleDTO -> {
                StaffRoleDTO staffRoleDTO = new StaffRoleDTO();
                BeanUtils.copyProperties(roleDTO, staffRoleDTO);
                staffRoles.add(staffRoleDTO);
            });
        }
        staffDTO.setRoles(staffRoles);
        return staffDTO;
    }

    /**
     * 获取员工详情
     *
     * @param id
     * @return
     */
    public StaffDTO queryStaffById(Long id) {
        Staff staff = staffMapper.selectById(id);
        if (staff == null) {
            throw new BusinessException(CommonErrorCode.E_200013);
        }
        StaffDTO staffDTO = StaffConvert.INSTANCE.entity2Dto(staff);
        //根据员工归属门店ID，获取门店信息
        StoreDTO store = queryStoreById(staff.getStoreId());
        staffDTO.setStoreName(store.getStoreName());
        return staffDTO;
    }

    @Override
    public void modifyStaff(StaffDTO staffDTO, String[] roleCodes) throws BusinessException {
        Staff staff = staffMapper.selectById(staffDTO.getId());
        if (staff == null) {
            throw new BusinessException(CommonErrorCode.E_200013);
        }
        //更新员工的信息
        staff.setFullName(staffDTO.getFullName());
        staff.setPosition(staffDTO.getPosition());
        staff.setStoreId(staffDTO.getStoreId());
        staffMapper.updateById(staff);

        //处理员工的角色是否有修改
        Long tenantId = queryMerchantById(staff.getMerchantId()).getTenantId();
        tenantService.getAccountRoleBind(staff.getUsername(), tenantId, roleCodes);
    }

    @Override
    @Transactional
    public void removeStaff(Long id) throws BusinessException {
        Staff staff = staffMapper.selectById(id);
        //清除员工和门店的关系
        storeStaffMapper.delete(new QueryWrapper<StoreStaff>().lambda().eq(StoreStaff::getStaffId, staff.getId()));
        //删除员工对应的账号，账号-角色之前的关系
        //获取商户的租户ID
        Long tenantId = queryMerchantById(staff.getMerchantId()).getTenantId();
        tenantService.unbindTenant(tenantId, staff.getUsername());       //将某个账号从租户内移除，租户管理员不可移除

        //删除员工
        staffMapper.deleteById(staff);
    }

    /**
     * 员工手机号在同一个商户下是唯一校验
     *
     * @param mobile
     * @param merchantId
     * @return
     */
    private Boolean isExistStaffByMobile(String mobile, Long merchantId) {
        int count = staffMapper.selectCount(new LambdaQueryWrapper<Staff>().eq(Staff::getMobile, mobile).eq(Staff::getMerchantId, merchantId));
        return count > 0 ? true : false;
    }

    /**
     * 员工账号是否在该商户下存在
     *
     * @param username   员工账号
     * @param merchantId 商户ID
     * @return
     */
    private Boolean isExistStaffByUsername(String username, Long merchantId) {
        int count = staffMapper.selectCount(new LambdaQueryWrapper<Staff>().eq(Staff::getUsername, username));
        return count > 0 ? true : false;
    }
}
